package com.imax.center.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.xiaoymin.knife4j.core.util.StrUtil;
import com.imax.center.constant.H3yunApiConstants;
import com.imax.center.entity.SensorEventData;
import com.imax.center.domain.AlarmConfig;
import com.imax.center.service.*;
import com.imax.center.service.impl.h3yun.H3yunOperate;
import com.imax.center.websocket.WsEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import com.imax.center.utils.MyFileUtils;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

@Component
@Slf4j
public class DeviceDataServiceImpl implements DeviceDataService {

    @Autowired
    H3yunOperate h3yunOperate;

    @Autowired
    SensorEventDataService sensorEventDataService;
    
    @Autowired
    DataCacheService dataCacheService;

    @Autowired
    DeviceService deviceService;

    @Autowired
    CommonService commonService;

    @Autowired
    WsEndpoint wsEndpoint;

    @Value("${config.saveToH3yunEnable}")
    private Boolean saveToH3yunEnable;

    @Value("${config.saveToH3yunCnList}")
    private String saveToH3yunCnList;

    @Value("${config.realDataSaveDays}")
    private Integer realDataSaveDays;

    @Value("${config.minuteDataSaveDays}")
    private Integer minuteDataSaveDays;

    @Value("${tcp.log.enable}")
    private boolean logEnable;

    private static final String DEFAULT_HANDLER_STATUS = "未处置";
    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");

    private void logOut(String message, Object... args) {
        if (logEnable) {
            log.info(message, args);
        }
    }

    @Override
    public void syncHandlerEvent(String message, Map<String, Object> parsedData) {
        this.eventHandler(message, parsedData);
    }

    private void eventHandler(String message, Map<String, Object> parsedData) {
        logOut("mq data received message: {}", message);

        // 1.推送报文到页面，展示到视频画面
        this.pushEventDataToVideo(parsedData);

        // 2.事件报文写入h3yun
        if (parsedData.get("CN") != null && saveToH3yunEnable && saveToH3yunCnList.contains(parsedData.get("CN").toString())) {
            this.addEventDataToh3yun(message, parsedData);
        }

        // 3.判断是否生成预警事件并推送到页面
        this.checkAlarmEvent(parsedData);

        logOut("eventData handler finished");
    }

    private void pushEventDataToVideo(Map<String, Object> data) {
        //如果parsedData.CN值在指定数组中才推送
        List<String> pushTypeList = new ArrayList<>(Arrays.asList("2011", "2051", "2061", "2031")); //实时数据、分钟数据、小时数据、日数据
        String CN = (String) data.get("CN");
        if (pushTypeList.contains(CN)) {
            // 构建推送消息
            JSONObject alarmMessage = new JSONObject();
            alarmMessage.put("type", "eventData");
            alarmMessage.put("data", data);
            // 推送报文到页面，展示到视频画面
            wsEndpoint.sendMessageToAllUser(alarmMessage);
            logOut("报文推送成功");
        }
    }
    
    /**
     * 检查是否需要生成预警事件
     * @param data 设备上报数据
     */
    private void checkAlarmEvent(Map<String, Object> data) {
        if (data == null || data.isEmpty() || data.get("CP") == null) {
            return;
        }
        
        try {
            // 获取站点配置
            List<AlarmConfig> configList = dataCacheService.getAlarmConfigList();
            if (configList == null || configList.isEmpty()) {
                logOut("未获取到站点配置，无法进行预警判断");
                return;
            }

            //传感器设备
            JSONArray sensorDevice = dataCacheService.getSensorDevice();
            if (sensorDevice == null || sensorDevice.isEmpty()) {
                logOut("未获取到传感器设备信息，无法创建预警事件");
                return;
            }

            String deviceMN = data.get("MN") != null ? data.get("MN").toString() : null;

            //根据MN号获取传感器设备信息
            JSONObject sensorDeviceObj = null;
            for (int i = 0; i < sensorDevice.size(); i++) {
                JSONObject device = sensorDevice.getJSONObject(i);
                if (device.getString("DeviceCode").equals(deviceMN)) {
                    sensorDeviceObj = device;
                    break;
                }
            }
            if (sensorDeviceObj == null) {
                logOut("未找到MN号对应的传感器设备信息，MN号：{}", deviceMN);
                return;
            }
            
            //根据OrgId遍历获取站点配置
            String orgId = sensorDeviceObj.getString("OrgId");
            if (orgId == null || orgId.isEmpty()) {
                logOut("传感器设备信息中未找到OrgId，无法进行预警判断");
                return;
            }
            //过滤出当前单位的站点配置列表
            List<AlarmConfig> configs = new ArrayList<>();
            for (AlarmConfig item : configList) {
                if (orgId.equals(item.getOrgId())) {
                    //MaxField 或 MinField不为null且不为空
                    if (item.getMaxField() != null && !item.getMaxField().isEmpty() || item.getMinField() != null && !item.getMinField().isEmpty()) {
                        configs.add(item);
                    }
                }
            }
            if (configs.isEmpty()) {
                logOut("未找到OrgId对应的站点配置，OrgId：{}", orgId);
                return;
            }
            
            Map<String, Object> cpMap = this.getCpData(data.get("CP"));
            
            //根据配置生成预警事件
            buildAlarmEvent(cpMap, sensorDeviceObj, configs);
            
        } catch (Exception e) {
            log.error("检查预警事件异常", e);
        }
    }

    private Map<String, Object> getCpData(Object cpObj) {
        Map<String, Object> cpMap = null;
        if (cpObj instanceof Map) {
            cpMap = (Map<String, Object>) cpObj;
        } else if (cpObj instanceof String) {
            cpMap = JSONObject.parseObject((String) cpObj);
        } else {
            log.error("CP数据格式异常: {}", cpObj);
        }
        return cpMap;
    }
    
    /**
     * 根据配置生成预警事件
     */
    private void buildAlarmEvent(Map<String, Object> cpMap, JSONObject sensorDeviceObj, List<AlarmConfig> configs) {
        if (cpMap == null || cpMap.isEmpty() || configs == null || configs.isEmpty()) {
            logOut("cpMap数据或预警配置为空，无法生成预警事件");
            return;
        }
        
        try {
            for (AlarmConfig config : configs) {
                // 获取配置的检测字段，包括MaxField和MinField
                String maxField = config.getMaxField();
                String minField = config.getMinField();
                
                // 获取检测字段的值，转换为Double
                Object maxFieldValueObj = maxField != null && !maxField.isEmpty() ? cpMap.get(maxField) : null;
                Object minFieldValueObj = minField != null && !minField.isEmpty() ? cpMap.get(minField) : null;
                if (maxFieldValueObj == null && minFieldValueObj == null) {
                    continue;
                }
                
                double maxValue = 0;
                double minValue = 0;
                try {
                    //报文最大值
                    if (maxFieldValueObj != null) {
                        if (maxFieldValueObj instanceof Number) {
                            maxValue = ((Number) maxFieldValueObj).doubleValue();
                        } else {
                            maxValue = Double.parseDouble(maxFieldValueObj.toString());
                        }
                    }
                    //报文最小值
                    if (minFieldValueObj != null) {
                        if (minFieldValueObj instanceof Number) {
                            minValue = ((Number) minFieldValueObj).doubleValue();
                        } else {
                            minValue = Double.parseDouble(minFieldValueObj.toString());
                        }
                    }
                } catch (NumberFormatException e) {
                    log.error("字段值转换为数值类型失败: {}", maxFieldValueObj);
                    continue;
                }
                
                // 获取配置的预警阈值, 且需要判空处理
                double maxWarnConfig = config.getMaxWarnValue() != null ? config.getMaxWarnValue() : 0;
                double minWarnConfig = config.getMinWarnValue() != null ? config.getMinWarnValue() : 0;
                double maxAlarmConfig = config.getMaxAlarmValue() != null ? config.getMaxAlarmValue() : 0;
                double minAlarmConfig = config.getMinAlarmValue() != null ? config.getMinAlarmValue() : 0;
                String operate = config.getOperate(); //大于等于;小于等于
                
                boolean isWarning = false;
                boolean isAlarm = false;
                String alarmLevel = "";
                
                // 根据操作符判断是否触发预警，使用包含判断

                //上限要求时
                if (operate.contains("大于等于")) {
                    if (maxValue >= maxAlarmConfig) {
                        isAlarm = true;
                        alarmLevel = "超标";
                    } else if (maxValue >= maxWarnConfig) {
                        isWarning = true;
                        alarmLevel = "异常";
                    }
                    // 如果触发预警
                    if (isWarning || isAlarm) {
                        String alarmTypeName = config.getCheckName() + alarmLevel;
                        String alarmRemark = config.getCheckName() + "(" + config.getMaxField().split("-")[1] + ")";
                        createAlarmEvent(alarmTypeName, alarmLevel, maxValue, sensorDeviceObj, cpMap, alarmRemark);
                    }
                }

                //重置isWarning和isAlarm
                isWarning = false;
                isAlarm = false;

                //下限要求时
                if (operate.contains("小于等于")) {
                    if (minValue <= minAlarmConfig) {
                        isAlarm = true;
                        alarmLevel = "超标";
                    } else if (minValue <= minWarnConfig) {
                        isWarning = true;
                        alarmLevel = "异常";
                    }
                    // 如果触发预警
                    if (isWarning || isAlarm) {
                        String alarmTypeName = config.getCheckName() + alarmLevel;
                        String alarmRemark = config.getCheckName() + "(" + config.getMinField().split("-")[1] + ")";
                        createAlarmEvent(alarmTypeName, alarmLevel, minValue, sensorDeviceObj, cpMap, alarmRemark);
                    }
                }
            }
        } catch (Exception e) {
            log.error("生成预警事件异常", e);
        }
    }
    
    /**
     * 创建预警事件
     */
    private void createAlarmEvent(String alarmTypeName, String alarmLevel, double alarmValue, JSONObject sensorDeviceObj, Map<String, Object> cpMap, String alarmRemark) {
        try {
            JSONObject bizObject = new JSONObject();
            bizObject.put("AlarmType", alarmTypeName);
            bizObject.put("AlarmTypeName", alarmTypeName);
            bizObject.put("OrgId", sensorDeviceObj.getString("OrgId"));
            bizObject.put("SensorDeviceId", sensorDeviceObj.getString("ObjectId"));
            bizObject.put("SensorDeviceName", sensorDeviceObj.getString("DeviceName"));
            bizObject.put("ChannelId", sensorDeviceObj.getString("ChannelId"));
            bizObject.put("AlarmValue", alarmValue);
            bizObject.put("AlarmTime", this.formatDataTime((String)cpMap.get("DataTime")).format(DATE_TIME_FORMATTER));
            bizObject.put("AlarmLevel", alarmLevel);
            bizObject.put("HandlerStatus", DEFAULT_HANDLER_STATUS);
            bizObject.put("EventData", JSONObject.toJSONString(cpMap));

            // 创建预警事件到H3云平台
            JSONObject saveObject = h3yunOperate.CreateBizObject(H3yunApiConstants.SCHEMA_ALARM_EVENT, bizObject);
            String bizObjectId = saveObject.getString("BizObjectId");
            logOut("预警事件创建成功：{}", bizObject);

            //设备抓拍图片
            String captureImage = deviceService.captureDeviceImage(sensorDeviceObj.getString("DeviceSerial"), sensorDeviceObj.getInteger("ChannelNo"));

            //文件上传，需要有了bizObjectId才能添加，将filePath转换为MultipartFile
            MultipartFile multipartFile = MyFileUtils.urlToMultipartFile(captureImage);
            // 上传文件到本地-大图
            Map<String, Object> imgMap = new HashMap<>();
            imgMap.put(alarmRemark, alarmValue);
            String attachmentPreviewUrl = commonService.saveImageDrawEle(multipartFile, imgMap, null);
            multipartFile = MyFileUtils.urlToMultipartFile(attachmentPreviewUrl);
            // 上传文件到氚云
            String attachmentId = h3yunOperate.uploadAttachment(H3yunApiConstants.SCHEMA_ALARM_EVENT, "AlarmSnap", bizObjectId, multipartFile);
            logOut("预警事件图片上传成功：{}", attachmentId);

            // 获取缩略图URL
            String thumbnailUrl = h3yunOperate.getThumbnailUrl(attachmentId);
            MultipartFile thumbnailFile = MyFileUtils.urlToMultipartFile(thumbnailUrl);
            // 同时上传文件到本地-缩略图
            String thumbnailPreviewUrl = commonService.saveImage(thumbnailFile, attachmentId + "_thumbnail");

            //更新图片url
            bizObject.put("AlarmPic", attachmentPreviewUrl);
            bizObject.put("ThumbnailUrl", thumbnailPreviewUrl);
            //更新预警事件
            JSONObject updateObject = h3yunOperate.UpdateBizObject(H3yunApiConstants.SCHEMA_ALARM_EVENT, bizObjectId, bizObject);
            logOut("预警事件更新成功：{}", updateObject);

            // 向所有连接的WebSocket客户端推送预警事件消息
            try {
                // 构建推送消息
                JSONObject alarmMessage = new JSONObject();
                alarmMessage.put("type", "alarm");
                alarmMessage.put("data", bizObject);

                // 推送消息
                wsEndpoint.sendMessageToAllUser(alarmMessage);
                logOut("预警事件推送成功");
            } catch (Exception e) {
                log.error("推送预警事件消息异常", e);
            }
        } catch (Exception e) {
            log.error("创建预警事件异常", e);
        }
    }

    private LocalDateTime formatDataTime(String val) {
        //"20250520220405"转为"yyyy/MM/dd HH:mm:ss"
        if (StrUtil.isBlank(val) || val.length() != 14) {
            return LocalDateTime.now();
        }
        // 输入格式
        DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
        return LocalDateTime.parse(val, inputFormatter);
    }

    private void addEventDataToh3yun(String message, Map<String, Object> data) {
        //data判空
        if (data == null || data.isEmpty()) {
            logOut("data is null");
            return;
        }
        Map<String, String> cnMap = new HashMap<>();
        cnMap.put("2011", "实时数据");
        cnMap.put("2051", "分钟数据");
        cnMap.put("2061", "小时数据");
        cnMap.put("2031", "日数据");
        try {
            String OrgId = "";
            String OrgDingDingDept = "";
            JSONArray sensorDeviceList = dataCacheService.getSensorDevice();
            //遍历,根据DeviceCode字段和MN字段匹配
            for (int i = 0; i < sensorDeviceList.size(); i++) {
                JSONObject sensorDevice = sensorDeviceList.getJSONObject(i);
                if (sensorDevice.getString("DeviceCode").equals(data.get("MN"))) {
                    OrgId = sensorDevice.getString("OrgId");
                    OrgDingDingDept = sensorDevice.getString("OrgDingDingDept");
                }
            }

            Map<String, Object> cpMap = this.getCpData(data.get("CP"));
            Double w00000Rtd = (Double) cpMap.get("w00000-Rtd"); //流量瞬时值
            Double w00000Avg = (Double) cpMap.get("w00000-Avg"); //流量平均值
            Double w00000Cou = (Double) cpMap.get("w00000-Cou"); //流量累积值
            String w00000Flag = (String) cpMap.get("w00000-Flag"); //流量标记值
            // 构建业务对象数据
            JSONObject bizObject = new JSONObject();
            bizObject.put("Header", data.get("Header"));
            bizObject.put("DataLength", data.get("DataLength"));
            bizObject.put("QN", data.get("QN"));
            bizObject.put("st", data.get("ST"));
            bizObject.put("CN", data.get("CN"));
            bizObject.put("CNText", cnMap.get(data.get("CN")));
            bizObject.put("PW", data.get("PW"));
            bizObject.put("MN", data.get("MN"));
            bizObject.put("Flag", data.get("Flag"));
            bizObject.put("CP", JSONObject.toJSONString(data.get("CP")));
            bizObject.put("CRC16", data.get("CRC16"));
            bizObject.put("eventData", JSONObject.toJSONString(data));
            bizObject.put("eventDataStr", message);
            bizObject.put("upTime", this.formatDataTime((String)cpMap.get("DataTime")));
            String dataTime = (String)cpMap.get("DataTime");
            //格式：YYYY-MM-DD HH:mm:ss
            bizObject.put("upTimeStr", dataTime.substring(0, 4) + "-" + dataTime.substring(4, 6) + "-" + dataTime.substring(6, 8) + " " + dataTime.substring(8, 10) + ":" + dataTime.substring(10, 12) + ":" + dataTime.substring(12, 14));
            //如果是实时数据，则保存一天
            if (data.get("CN").equals("2011")) {
                bizObject.put("delTime", this.formatDataTime((String)cpMap.get("DataTime")).plusDays(realDataSaveDays));
                //实时数据 将w00000Rtd的值赋值给w00000Cou和w00000Avg
                w00000Cou = w00000Rtd;
                w00000Avg = w00000Rtd;
            }
            //如果是分钟数据，则保存2天
            if (data.get("CN").equals("2051")) {
                bizObject.put("delTime", this.formatDataTime((String)cpMap.get("DataTime")).plusDays(minuteDataSaveDays));
            }
            //如果是小时数据和日期数据，则不删除
            if (data.get("CN").equals("2061") || data.get("CN").equals("2031")) {
                bizObject.put("delTime", null);
            }
            bizObject.put("w00000Rtd", w00000Rtd);
            bizObject.put("w00000Avg", w00000Avg);
            bizObject.put("w00000Cou", w00000Cou);
            bizObject.put("w00000Flag", w00000Flag);
            bizObject.put("OrgId", OrgId);
            bizObject.put("OrgDingDingDept", OrgDingDingDept);
            h3yunOperate.CreateBizObject(H3yunApiConstants.SCHEMA_SENSOR_EVENT_DATA, bizObject);
        } catch (Exception e) {
            log.error("add eventData to h3yun error", e);
        }
    }

}
